import anime from 'animejs'
import { edgeConfig } from '../config/edgeConfig'
import { sizeConfig } from '../config/sizeConfig'
import {
    getPcToMultiplexerPoints,
    getRamToMultiplexer21Points,
    getRegDToMultiplexer31AndAuBPoints,
    getRegSToMultiplexer31AndAuAPoints,
} from '../shape/bus'
import { IDataValue, IModule } from '../types'
import { createDataValue, getCenterPoint, toBinaryStr } from '../utils'
import { au } from './au'
import { multiplexer21 } from './mutiplexer21'
import { multiplexer31 } from './mutiplexer31'
import { output } from './output'
import { runtimeConfig } from '../config/runtimeConfig'

interface IBus extends IModule {
    data: number
    dataItems: IDataValue<number>[]
    setData: (data: number) => Promise<void>
    getData: () => number
}

interface IBus1 extends Omit<IBus, 'setData'> {
    setData: (
        data: number,
        from: 'ram' | 'au' | 'input'
    ) => Promise<void[] | void>
    setDataFromRam: () => Promise<void[]>
    setDataFromAu: () => Promise<void[]>
    setDataFromInput: () => Promise<void[]>
}

/**
 * 1号总线，从ram的上方或au的上方或外部输入出发，到ir、ram、2-1选择器、输出
 */
export const bus1: IBus1 = {
    name: 'bus1',
    data: 0,
    dataItems: [],
    async setData(data: number, from: 'ram' | 'au' | 'input') {
        this.data = data
        const text = toBinaryStr(this.data)
        // 当总线数据发生变化时，数据会从ram的上方或au的上方或外部输入出发，到ir、ram、2-1选择器、输出
        for (const it of this.dataItems) {
            it.setText(text)
        }
        if (from === 'ram') {
            return this.setDataFromRam()
        } else if (from === 'au') {
            return this.setDataFromAu()
        } else if (from === 'input') {
            return this.setDataFromInput()
        }
        return
    },
    async setDataFromRam() {
        const { point1, point2, point3, point4, point5, point7 } =
            getRamToMultiplexer21Points()
        // 把所有的点放到ram的上方
        const initialPosition = {
            x: point1.x,
            y: point1.y - 5,
        }
        for (const it of this.dataItems) {
            it.setPosition(initialPosition)
        }
        // 从ram的上方到2-1选择器
        const timeline1 = anime
            .timeline({
                targets: this.dataItems[2].dom,
                easing: 'linear',
                duration: (edgeConfig.time.rise + edgeConfig.time.high) * 0.2,
            })
            .add({
                translateX: point2.x,
            })
            .add({
                translateY: point4.y - 10,
            })
            .add({
                translateX: point4.x + 4.5,
                duration: (edgeConfig.time.rise + edgeConfig.time.high) * 0.1,
            })
            .add({
                translateY: point3.y,
                duration: (edgeConfig.time.rise + edgeConfig.time.high) * 0.05,
                complete: () => {
                    if (multiplexer21.state.s0 == 1) {
                        multiplexer21.change!()
                    }
                },
            })
        // 从ram的上方到输出
        const timeline2 = anime
            .timeline({
                targets: this.dataItems[1].dom,
                easing: 'linear',
                duration: (edgeConfig.time.rise + edgeConfig.time.high) * 0.15,
                complete: () => {
                    output.change!()
                },
            })
            .add({
                translateX: point2.x,
            })
            .add({
                translateY: point7.y - 5,
            })
            .add({
                translateX: point7.x - 30,
            })
        // 从ram的上方到ir
        const timeline3 = anime
            .timeline({
                targets: this.dataItems[0].dom,
                easing: 'linear',
                duration: (edgeConfig.time.rise + edgeConfig.time.high) * 0.1,
            })
            .add({
                translateX: point5.x + 4.5,
            })
            .add({
                translateY: point5.y,
            })
        return Promise.all([
            timeline1.finished,
            timeline2.finished,
            timeline3.finished,
        ])
    },
    async setDataFromAu() {
        const { point1, point2, point3, point4, point5, point7 } =
            getRamToMultiplexer21Points()
        // au上方的点
        const point8 = getCenterPoint(sizeConfig.au, 'top')
        // 把所有的点放到au的上方
        const initialPosition = {
            x: point8.x,
            y: point1.y - 5,
        }
        for (const it of this.dataItems) {
            it.setPosition(initialPosition)
        }
        // 从au的上方到2-1选择器
        const timeline1 = anime
            .timeline({
                targets: this.dataItems[2].dom,
                easing: 'linear',
                duration: (edgeConfig.time.rise + edgeConfig.time.high) * 0.25,
            })
            .add({
                translateX: point2.x,
            })
            .add({
                translateY: point4.y - 10,
                duration: (edgeConfig.time.rise + edgeConfig.time.high) * 0.2,
            })
            .add({
                translateX: point4.x + 4.5,
                duration: (edgeConfig.time.rise + edgeConfig.time.high) * 0.05,
            })
            .add({
                translateY: point3.y,
                duration: (edgeConfig.time.rise + edgeConfig.time.high) * 0.05,
                complete: () => {
                    multiplexer21.change!()
                },
            })
        // 从au的上方到输出
        const timeline2 = anime
            .timeline({
                targets: this.dataItems[1].dom,
                easing: 'linear',
                duration: (edgeConfig.time.rise + edgeConfig.time.high) * 0.15,
                complete: () => {
                    output.change!()
                },
            })
            .add({
                translateX: point2.x,
            })
            .add({
                translateY: point7.y - 5,
            })
            .add({
                translateX: point7.x - 30,
            })
        // 从au的上方到ir
        const timeline3 = anime
            .timeline({
                targets: this.dataItems[0].dom,
                easing: 'linear',
                duration: (edgeConfig.time.rise + edgeConfig.time.high) * 0.05,
            })
            .add({
                translateX: point5.x + 4.5,
            })
            .add({
                translateY: point5.y,
            })
        // 从au的上方到ram
        const timeline4 = anime
            .timeline({
                targets: this.dataItems[3].dom,
                easing: 'linear',
                duration: (edgeConfig.time.rise + edgeConfig.time.high) * 0.1,
            })
            .add({
                translateX: point1.x,
            })

        return Promise.all([
            timeline1.finished,
            timeline2.finished,
            timeline3.finished,
            timeline4.finished,
        ])
    },
    async setDataFromInput() {
        const { point1, point2, point3, point4, point5, point6, point7 } =
            getRamToMultiplexer21Points()
        // 把所有的点放到input的左边
        const initialPosition = {
            x: point6.x - 30,
            y: point6.y - 5,
        }
        for (const it of this.dataItems) {
            it.setPosition(initialPosition)
        }
        // 从input的左边到2-1选择器
        const timeline1 = anime
            .timeline({
                targets: this.dataItems[2].dom,
                easing: 'linear',
                duration: (edgeConfig.time.rise + edgeConfig.time.high) * 0.2,
            })
            .add({
                translateX: point2.x,
            })
            .add({
                translateY: point4.y - 10,
            })
            .add({
                translateX: point4.x + 4.5,
            })
            .add({
                translateY: point3.y,
                complete: () => {
                    multiplexer21.change!()
                },
            })
        // 从input的左边到输出
        const timeline2 = anime
            .timeline({
                targets: this.dataItems[1].dom,
                easing: 'linear',
                duration: (edgeConfig.time.rise + edgeConfig.time.high) * 0.05,
                complete: () => {
                    output.change!()
                },
            })
            .add({
                translateX: point2.x,
            })
            .add({
                translateY: point7.y - 5,
            })
            .add({
                translateX: point7.x - 30,
            })
        // 从input的左边到ir
        const timeline3 = anime
            .timeline({
                targets: this.dataItems[0].dom,
                easing: 'linear',
                duration: (edgeConfig.time.rise + edgeConfig.time.high) * 0.15,
            })
            .add({
                translateX: point2.x,
            })
            .add({
                translateY: point2.y - 5,
            })
            .add({
                translateX: point5.x + 4.5,
            })
            .add({
                translateY: point5.y,
            })
        // 从input的左边到ram
        const timeline4 = anime
            .timeline({
                targets: this.dataItems[3].dom,
                easing: 'linear',
                duration: (edgeConfig.time.rise + edgeConfig.time.high) * 0.2,
            })
            .add({
                translateX: point2.x,
            })
            .add({
                translateY: point2.y - 5,
            })
            .add({
                translateX: point1.x,
            })

        return Promise.all([
            timeline1.finished,
            timeline2.finished,
            timeline3.finished,
            timeline4.finished,
        ])
    },
    getData() {
        return this.data
    },
    init() {
        // 获取pc的中心点
        const text = toBinaryStr(this.data)
        const { point1, point5, point7, point3 } = getRamToMultiplexer21Points()
        // 初始化四个数据值
        // 到ram的上方
        this.dataItems[0] = createDataValue(this.data, text, point1)
        // ir下方的点
        this.dataItems[1] = createDataValue(this.data, text, point5)
        // 输出左边的点
        point7.x -= 30
        this.dataItems[2] = createDataValue(this.data, text, point7)
        // 2-1下面的点
        this.dataItems[3] = createDataValue(this.data, text, point3)
        this.setData(0, 'ram')
    },
}

/**
 * 2号总线，从PC的出口到3-1多路选择器的入口和2-1多路选择器的入口
 */
export const bus2: IBus = {
    name: 'bus2',
    data: 0,
    dataItems: [],
    async setData(data: number) {
        this.data = data
        const pcTopCenter = getCenterPoint(sizeConfig.pc, 'top')
        pcTopCenter.y -= 10
        const text = toBinaryStr(this.data)
        // 当总线数据发生变化时，数据会从PC的出口到3-1多路选择器的入口和2-1多路选择器的入口
        const timeline1 = anime.timeline({
            targets: this.dataItems[0].dom,
            easing: 'linear',
            duration: (edgeConfig.time.neg + edgeConfig.time.low) * 0.1,
        })

        const { point2, point3, point4, point5 } = getPcToMultiplexerPoints()

        // 从PC的出口到3-1多路选择器的入口
        this.dataItems[0].setText(text)
        if (edgeConfig.time.neg + edgeConfig.time.low > 0) {
            this.dataItems[0].setPosition(pcTopCenter)
        }

        timeline1.add({
            translateY: point5.y,
            translateX: point5.x - 4.5,
            complete: () => {
                const { s1, s2 } = multiplexer31.state
                if (s1 === 0 && s2 === 0) {
                    multiplexer31.change!()
                }
            },
        })

        // 从PC的出口到2-1多路选择器的入口
        const timeline2 = anime.timeline({
            targets: this.dataItems[1].dom,
            easing: 'linear',
            duration: (edgeConfig.time.neg + edgeConfig.time.low) * 0.1,
        })

        this.dataItems[1].setText(text)
        if (edgeConfig.time.neg + edgeConfig.time.low > 0) {
            this.dataItems[1].setPosition(pcTopCenter)
        }

        timeline2
            .add({
                translateY: point2.y - 10,
            })
            .add({
                translateX: point2.x,
            })
            .add({
                translateY: point4.y - 10,
            })
            .add({
                translateX: point4.x - 4.5,
            })
            .add({
                translateY: point3.y,
                complete: () => {
                    multiplexer21.change!()
                },
            })
    },
    getData() {
        return this.data
    },
    init() {
        // 获取pc的中心点
        const pcTopCenter = getCenterPoint(sizeConfig.pc, 'top')
        pcTopCenter.y -= 10
        const text = toBinaryStr(this.data)
        // 初始化两个数据值
        this.dataItems[0] = createDataValue(this.data, text, pcTopCenter)
        this.dataItems[1] = createDataValue(this.data, text, pcTopCenter)
        this.setData(0)
    },
}

/**
 * 3号总线，从通用寄存器的S口到3-1选择器2口和AU的A口、PC的入口
 */
export const bus3: IBus = {
    name: 'bus3',
    data: 0,
    dataItems: [],
    async setData(data: number) {
        this.data = data
        const text = toBinaryStr(this.data)
        // 当总线数据发生变化时，数据会从通用寄存器的S口到3-1选择器2口和AU的A口、PC的入口
        const { point1, point2, point3, point4, point5, point6, point7 } =
            getRegSToMultiplexer31AndAuAPoints()
        const regSTopCenter = {
            x: point1.x + 4.5,
            y: point1.y - 10,
        }
        for (const it of this.dataItems) {
            it.setText(text)
            it.setPosition(regSTopCenter)
        }
        // 到pc的入口
        const timeline1 = anime.timeline({
            targets: this.dataItems[0].dom,
            easing: 'linear',
            duration: (edgeConfig.time.neg + edgeConfig.time.low) * 0.15,
        })
        timeline1
            .add({
                translateY: point4.y - 10,
                duration: (edgeConfig.time.neg + edgeConfig.time.low) * 0.05,
            })
            .add({
                translateX: point5.x,
            })
            .add({
                translateY: point7.y - 10,
            })
            .add({
                translateX: point7.x + 4.5,
                duration: (edgeConfig.time.neg + edgeConfig.time.low) * 0.1,
            })
            .add({
                translateY: point6.y,
                duration: (edgeConfig.time.neg + edgeConfig.time.low) * 0.05,
            })
        // 到3-1选择器的2口
        const timeline2 = anime.timeline({
            targets: this.dataItems[1].dom,
            easing: 'linear',
            duration: (edgeConfig.time.neg + edgeConfig.time.low) * 0.15,
        })
        timeline2
            .add({
                translateY: point4.y - 10,
                duration: (edgeConfig.time.neg + edgeConfig.time.low) * 0.05,
            })
            .add({
                translateX: point4.x + 4.5,
            })
            .add({
                translateY:
                    point2.y + (runtimeConfig.dataDisplayRadix === 2 ? 10 : 0),
                complete: () => {
                    const { s1, s2 } = multiplexer31.state
                    if (s1 === 1 && s2 === 0) {
                        multiplexer31.change!()
                    }
                },
            })
        // 到au的A口
        anime.remove(this.dataItems[2].dom)
        const timeline3 = anime.timeline({
            targets: this.dataItems[2].dom,
            easing: 'linear',
            duration: (edgeConfig.time.neg + edgeConfig.time.low) * 0.15,
        })
        timeline3.add({
            translateY: point3.y,
            complete: () => {
                au.change!()
            },
        })
    },
    getData() {
        return this.data
    },
    init() {
        // 获取reg的S口中心点
        const regSTopCenter = getCenterPoint(sizeConfig.reg, 'top')
        regSTopCenter.x -= sizeConfig.reg.width / 4
        regSTopCenter.y -= 10
        const text = toBinaryStr(this.data)
        // 初始化两个数据值
        this.dataItems[0] = createDataValue(this.data, text, regSTopCenter)
        this.dataItems[1] = createDataValue(this.data, text, regSTopCenter)
        this.dataItems[2] = createDataValue(this.data, text, regSTopCenter)
        this.setData(0)
    },
}

/**
 * 4号总线，从通用寄存器的D口到3-1选择器2口和AU的B口
 */
export const bus4: IBus = {
    name: 'bus4',
    data: 0,
    dataItems: [],
    async setData(data: number) {
        this.data = data
        const text = toBinaryStr(this.data)
        // 当总线数据发生变化时，数据会从通用寄存器的S口到3-1选择器2口和AU的A口、PC的入口
        const { point1, point2, point3, point4 } =
            getRegDToMultiplexer31AndAuBPoints()
        const regDTopCenter = {
            x: point1.x + 4.5,
            y: point1.y - 10,
        }
        for (const it of this.dataItems) {
            it.setText(text)
            it.setPosition(regDTopCenter)
        }
        // 到3-1选择器的2口
        const timeline1 = anime.timeline({
            targets: this.dataItems[0].dom,
            easing: 'linear',
            duration: (edgeConfig.time.neg + edgeConfig.time.low) * 0.15,
        })
        timeline1
            .add({
                translateY: point4.y - 10,
                duration: (edgeConfig.time.neg + edgeConfig.time.low) * 0.05,
            })
            .add({
                translateX: point4.x + 4.5,
            })
            .add({
                translateY: point2.y,
                complete: () => {
                    const { s1, s2 } = multiplexer31.state
                    if (s1 === 0 && s2 === 1) {
                        multiplexer31.change!()
                    }
                },
            })
        // 到au的B口
        anime.remove(this.dataItems[1].dom)
        const timeline2 = anime.timeline({
            targets: this.dataItems[1].dom,
            easing: 'linear',
            duration: (edgeConfig.time.neg + edgeConfig.time.low) * 0.15,
        })
        timeline2.add({
            translateY: point3.y,
            complete: () => {
                au.change!()
            },
        })
    },
    getData() {
        return this.data
    },
    init() {
        // 获取reg的D口中心点
        const regSTopCenter = getCenterPoint(sizeConfig.reg, 'top')
        regSTopCenter.x += sizeConfig.reg.width / 4
        regSTopCenter.y -= 10
        const text = toBinaryStr(this.data)
        // 初始化两个数据值
        this.dataItems[0] = createDataValue(this.data, text, regSTopCenter)
        this.dataItems[1] = createDataValue(this.data, text, regSTopCenter)
        this.setData(0)
    },
}
